/*
店铺签到，各类店铺签到，有新的店铺直接添加token即可
可设置变量jd_dpqd_tokens='A&B&C'
会和内置的token去重合并
每日最多签到22家店铺，超出失败
更新日期:2022-11-6
cron 3 0,23 * * * jd_dpsign.js, tag=店铺签到
*/
const $ = new Env('店铺签到11');
const fs = require('fs');
const { spawnSync } = require('child_process');

const notify = $.isNode() ? require('./6dylan6_jdpro/sendNotify') : '';
//Node.js用户请在jdCookie.js处填写京东ck;
const jdCookieNode = $.isNode() ? require('./6dylan6_jdpro/jdCookie.js') : '';
//IOS等用户直接用NobyDa的jd cookie
let cookiesArr = [], cookie = '', message;

const JD_API_HOST = 'https://api.m.jd.com/api?appid=interCenter_shopSign';

const ql = require('./ql');
const ev = ql.Ev;
const crons = ql.Crons;
fileAllName = '1jd_dpsign.js', unSignFile = '1未签到.txt', otherUnSignFile = '2未签到.txt', otherFileAllName = '2jd_dpsign.js'

let num = 0, doQuery = 0, isTest = 0
//店铺需要签到的天数1:[1,3,7]
let needSignDay = {};
//店铺完成签到的给的豆1:[5,10,100]
let getDou = {};
//要删除的token
let removeTk = [], finishTk = [];
//明天签到给豆的token
var changeToken = {};
//已成功签到的token 1:[t1, t2, t3]
var sucessTk = {};
//加上unsucessTkItem保存到文件的数据
var unsucessTk = { queryFail: [] };
//没有成功签到的token 1:[t1, t2, t3]
var unsucessTkItem = {};
//查询店铺接口返回的信息
var dpinfo = {};
//各账号需要签到的tkoen 1:[t1, t2, t3] 2:[t1, t2, t3]
var needSignToken = {};
let dptoken = []; //从环境变量取的token

if (process.env.jd_dpqd_tokens) {
    if (process.env.jd_dpqd_tokens.indexOf('\n') > -1) {
        dptoken = process.env.jd_dpqd_tokens.split('\n');
    } else if (process.env.jd_dpqd_tokens.indexOf('&') > -1) {
        dptoken = process.env.jd_dpqd_tokens.split('&');
    } else if (process.env.jd_dpqd_tokens.indexOf(',') > -1) {
        dptoken = process.env.jd_dpqd_tokens.split(',');
    } else {
        dptoken.push(process.env.jd_dpqd_tokens);
    }
}

dptoken = dptoken.filter(x => x !== '')
const token = [
]
const oneToken = [
]
const todayToken = [
    "E81A21A2A56632B88DCE1AC35D52B782",
"2FE3BB7B03C35839700D864D27DB8245",
"1541DF32552A1BEF333A9F991DC3BAC4",
"4A165221753604A16257C173F143451A",
"F090F49021E02F1A935AFF0F38A6DCD5",
"1D325AEDA8BA2FA07F13A8288C5D698D",
"30580EAB8B9A15099FB38814876DCAD0",
"3AD6262D3E31A3BF471337940A5CBF63",
"BB2B949C0095DD6F6558C279A7D7F00F",
"84F9080212620E604740F309FA014D60",
"2191E8F10F75D1C7F0E4CDC36D904B33",
"3908D84CA641A648D20D3BB917A139C8"
]

dpinfo = {}
var theEnd = '';
//参数初始化
const dpEvName = "jd_dpqd_tokens";
var tkInfoFile = './6dylan6_jdpro/rs_dpqd_tokens.json';
var tkInfoFilebak = './6dylan6_jdpro/rs_dpqd_tokens_bak.json';
var ttoday = new Date().getDay();
var modifyTkEv = process.env.jd_dpqd_tokens;
var allTkInfo = {};
if ($.isNode()) {
    Object.keys(jdCookieNode).forEach((item) => {
        cookiesArr.push(jdCookieNode[item])
    })
    if (process.env.JD_DEBUG && process.env.JD_DEBUG === 'false') console.log = () => { };
} else {
    let cookiesData = $.getdata('CookiesJD') || "[]";
    cookiesData = jsonParse(cookiesData);
    cookiesArr = cookiesData.map(item => item.cookie);
    cookiesArr.reverse();
    cookiesArr.push(...[$.getdata('CookieJD2'), $.getdata('CookieJD')]);
    cookiesArr.reverse();
    cookiesArr = cookiesArr.filter(item => item !== "" && item !== null && item !== undefined);
}
for (let index = 1; index < 22; index++) {
    needSignDay[index] = [];
    getDou[index] = []
}
var allMSg = {};
for (let index = 1; index <= cookiesArr.length; index++) {
    sucessTk[index] = [];
    unsucessTkItem[index] = [];
    allMSg[index] = "账号" + index + "\n\n";
}

var min = new Date().getMinutes();
var hour = new Date().getHours();
var isQuery = min > 13 || hour > 0;
var tryAgain = !(hour == 23 && min > 57 || hour == 0 && min < 3)
// tryAgain = false
isQuery = hour > 11;
if (tryAgain) {
    var content = fs.readFileSync(unSignFile, 'utf8');
    unsucessTk = JSON.parse(content);
    console.log('未签到', unsucessTk)
}
var hebing = false;
var hasNewToken = todayToken.length > 0;
!(async () => {
    var needSignList = [...token, ...oneToken, ...todayToken];
    needSignList = new Set(needSignList);
    needSignList = [...needSignList];
    for (let index = 1; index <= cookiesArr.length; index++) {
        if (tryAgain) {
            needSignToken[index] = [...todayToken];
        } else {
            needSignToken[index] = [...needSignList];
        }
    }
    //添加之前失败的tk进行重试
    if (unsucessTk[fileAllName] && unsucessTk['day'] === ttoday) {
        for (let [index, failTkList] of Object.entries(unsucessTk[fileAllName])) {
            if (failTkList && failTkList.length > 0) {
                needSignToken[index].push(...failTkList);
            }
        }
    }
    // console.log('needSignToken', needSignToken)
    // 执行查询
    if (hasNewToken || isQuery) {
        // if (0) {
        // 备份tkInfoFile到tkInfoFilebak
        if (fs.existsSync(tkInfoFile)) {
            fs.copyFileSync(tkInfoFile, tkInfoFilebak);
        } else {
            console.log(`文件 ${tkInfoFile} 不存在`);
        }
        //过滤已查询过的tk
        var unqueryTk = [...unsucessTk.queryFail];
        for (let value of needSignList) {
            if (dpinfo[value] == null || ttoday != dpinfo[value].ttoday) {
                unqueryTk.push(value)
            }
        }
        if (unqueryTk.length > 0) {
            unqueryTk = [...new Set(unqueryTk)];
            console.log('unqueryTk', unqueryTk)
            // 更新ev
            await ev.set(dpEvName, unqueryTk.join(','));
            await $.wait(1000)
            var runTask = '6dylan6_jdpro/jd_dpqd_main.js';
            await doRuntask(runTask, "查询");
            doQuery = 1;
            //解析日志
            var logDir = '/ql/data/log/6dylan6_jdpro_jd_dpqd_main_1911/';
            var content = getLastFileContent(logDir);

            //获取失效的tk
            var invalidTokens = getInvalidTokens(content)
            // console.log('getInvalidTokens', invalidTokens)
            removeTk.push(...invalidTokens);

            // 解析新的tkInfoFile 读取店铺活动信息
            var newtkStr = fs.readFileSync(tkInfoFile, 'utf8');
            var tkJson = JSON.parse(newtkStr);
            // console.log('tkJson', tkJson)
            if (Object.keys(tkJson).length == 0) {
                throw new Error('没有获取到tk信息')
            }
            allTkInfo = { ...tkJson };
            console.log("查到的key", Object.keys(tkJson));

            for (let [tk, info] of Object.entries(tkJson)) {
                // console.log("tk", tk, info);
                var activityId = info.activityId;
                var venderId = info.venderId;
                if (!venderId) {
                    console.log('查询失败', tk)
                    continue;
                }

                // 组装签到tk信息
                dpinfo[tk] = { 'activityId': activityId, 'vender': venderId };
                //3天3豆100份(无)|7天7豆100份
                var rules = info["prize"].split("|");
                var ruleDays = []
                var rulePrizes = []
                // 无奖品
                if (rules[rules.length - 1].indexOf('(无)') > -1) {
                    removeTk.push(tk);
                    console.log('奖品已无', tk)
                    continue;
                }
                for (let rule of rules) {
                    var match = rule.match(/(\d+|每)天(\d+)(豆|E卡)/);
                    // console.log('查询match', match)
                    if (match) {
                        if (match[1] == '每') {
                            ruleDays.push(30); // 捕获天数
                        } else {
                            ruleDays.push(match[1]); // 捕获天数
                        }
                        rulePrizes.push(match[2]); // 捕获豆数
                    } else {
                        console.log('获取豆-天失败', tk, rule);
                    }
                }
                dpinfo[tk].days = ruleDays
                dpinfo[tk].prizes = rulePrizes
                //今天查询过tk标志
                dpinfo[tk].ttoday = ttoday
                //console.log("dpinfo[tk]", tk, dpinfo[tk]);

            }
            //过滤needSignToken
            //  console.log('needSignToken1', needSignToken)
            for (let [key, value] of Object.entries(needSignToken)) {
                value = value.filter(tk => !removeTk.includes(tk));
                needSignToken[key] = value;
            }
            // console.log('needSignToken2', needSignToken)
        }
    }

    if (hasNewToken || !tryAgain) {
        // if (0) {
        if (hasNewToken) {
            //去掉请求失败unsucessTk.queryFail和失效invalidTokens的key
            let runTk = todayToken.filter(x=> !unsucessTk.queryFail.includes(x) || !removeTk.includes(x))
            await ev.set(dpEvName, runTk.join(','));
            await $.wait(1000)
        }
        //执行签到
        runTask = '6dylan6_jdpro/jd_dpqd_sign.js';
        await doRuntask(runTask, '签到');
        //解析签到结果
        var logDir = '/ql/data/log/6dylan6_jdpro_jd_dpqd_sign_1912/';
        //读取日志文件
        var content = getLastFileContent(logDir);
        if (0) {
            var logFile = logDir + '2024-11-13-00-00-01-309.log';
            content = fs.readFileSync(logFile, 'utf8');
        }
        analysisSignResults(content);
    }

    comboxQueryFile();

    await modifyToken()
    //输入日志
    var msg = ''
    for (let [key, value] of Object.entries(allMSg)) {
        msg += value;
    }
    try {
        var suNum = 0
        for (let i = 0; i < cookiesArr.length; i++) {
            if (sucessTk[i + 1]) {
                suNum = sucessTk[1].length
                break
            }
        }
        console.log('签到成功总数: ' + suNum)
        msg += '签到成功总数: ' + suNum
    } catch (e) {
        console.log('出错', e)
    }
    for (let [key, value] of Object.entries(sucessTk)) {
        if(value.length > 0) {
            notify.sendNotify('签到完成', msg);
            break;
        }
    }

})()
    .catch((e) => {
        console.log('出错', e)
        $.log('', `❌ ${$.name}, 失败! 原因: ${e}!`, '')
    })
    .finally(() => {
        //还原签到并发
        // ev.set("dpqd_concNum", "2")
        console.log('签到后更新ev token', modifyTkEv)
        ev.set(dpEvName, modifyTkEv)
        //若失败则重试
        if (!hebing) {
            comboxQueryFile();
        }
        $.done();
    })

// 获取失效的token
function getInvalidTokens(content) {
    try {
        // 匹配店铺信息块的正则
        const shopRegex = /店铺->\d+：([A-F0-9]{32})[\s\S]*?(?=店铺->|\n\s*查询信息缓存成功)/g;
        let invalidTokens = [];
        let match;
        while ((match = shopRegex.exec(content)) !== null) {
            const token = match[1];
            const shopInfo = match[0];

            // 检查是否包含"活动已失效"
            if (shopInfo.includes('活动已失效')) {
                invalidTokens.push(token);
                console.log(`发现失效token: ${token}`);
            }
            if (shopInfo.includes('请求失败')) {
                unsucessTk.queryFail.push(token);
                console.log(`查询失败token: ${token}`);
            }
        }
        return invalidTokens;
    } catch (error) {
        console.log('解析失效token出错:', error);
        return [];
    }
}

function analysisSignResults(exeResult) {
    // 提取店铺和签到详情的正则表达式
    const combinedRegex = /店铺->(\d+)[：:]\s*([A-F0-9]{32})[\s\S]*?[\s\S]*?(?=店铺->|\n\n|$)/g;

    // 提取具体签到记录的正则表达式
    const signDetailRegex = /┗━\[(\d+)\]\[(\w+)\] .*?已签(\d+)天/g;
    // 提取签到失败的正则表达式
    const failRegex = /签到API请求失败[\s\S]*?Response code (\d+)/g;

    // 存储解析结果
    let result = {};
    let pinMap = {
        q751654992: 1,
        jd_IAujVeaMgcoc: 2,
        jd_XEQNbCzaohvl: 3,
        jd_7e732dda05ce2: 4
    };
    // 解析店铺和签到详情
    let match;
    while ((match = combinedRegex.exec(exeResult)) !== null) {
        var matchContent = match[0];
        // console.log('matchContent', matchContent)
        const token = match[2];
        const section = match[1];
        console.log('签到详情', token, section);
        if (removeTk.includes(token)) {
            continue;
        }
        if (matchContent.includes('不存在有效的活动')) {
            removeTk.push(token)
            console.log('活动失效', token);
            continue;
        }
        if (matchContent.includes('缓存未命中')) {
            unsucessTk.queryFail.push(token);
            console.log(`签到有没查询的tk: ${token}`);
            continue;
        }

        result[token] = {};
        var doSignDay = 0;
        var isEnd = false
        var discount = 0;
        var needSignDay = dpinfo[token].days
        var getDou = dpinfo[token].prizes

        let signMatch;
        while ((signMatch = signDetailRegex.exec(matchContent)) !== null) {
            const pin = signMatch[2];
            const accNum = pinMap[pin];
            const signDay = parseInt(signMatch[3], 10);
            // console.log('accNum', accNum, pin, signDay);
            var message = ""

            if (signDay == 0) {
                continue;
            }
            result[token][accNum] = signDay;                //记录账号签到成功的tk
            sucessTk[accNum].push(token)
            //计算需要签到的天数
            for (let i = 0; i < needSignDay.length; i++) {
                discount = getDou[i];
                if (needSignDay.length == i + 1) {
                    doSignDay = needSignDay[i]
                    isEnd = true
                    break;
                }
                if (signDay < needSignDay[i]) {
                    doSignDay = needSignDay[i];
                    break;
                }
            }
            //计算差多少天
            var dis = doSignDay - signDay;
            console.log('账号' + accNum + '已签:' + signDay + ',需签:' + doSignDay + ',差' + dis + '天, 签完:' + isEnd + ', 任务:' + needSignDay + '\n')
            if (dis <= 0 && isEnd) {
                finishTk.push(token);
            } else if (dis == 1) {
                message += '第' + section + '家店 ' + token + ' 差1天得' + discount + '豆\n';
                //还有一天就签到完成的tk
                 if (finishTk.includes(token)) {
                    finishTk.splice(finishTk.indexOf(token), 1)
                }
                if (changeToken[discount]) {
                    changeToken[discount].push(token)
                } else {
                    changeToken[discount] = [token]
                }
            } else {
                //还有没签到完的
                if (finishTk.includes(token)) {
                    finishTk.splice(finishTk.indexOf(token), 1)
                }
                message += '第' + section + '家店再签' + dis + '天得' + discount + '豆\n';
            }
            allMSg[accNum] += message
        }
        // 解析签到失败记录
        // let failMatch;
        // while ((failMatch = failRegex.exec(section)) !== null) {
        //     //判断失败的是哪个账号
        //     const accNums = Array.from({length: cookiesArr.length}, (_, i) => i + 1);
        //     // 获取失败账号
        //     const failAccNum = accNums.find(num => result[token][num] === undefined);
        //     if (failAccNum) {
        //     }
        // }
    }
    return result;
}

async function modifyToken() {
    finishTk = finishTk.filter(rtk => {
        const ret = Object.keys(needSignToken).every(ckIdx => {
            let suTk = sucessTk[ckIdx];
            return suTk.includes(rtk);
        })
        // console.log('modifyToken 1 finishTk ret', rtk, ret)
        return ret;
    });
    //重跑没签成功的tk
    try {
        await doLastTk()
    } catch (e) {
        console.log('重跑没签成功的tk失败', e)
    }
    //重试完再次处理
    finishTk = finishTk.filter(rtk => {
        const ret = Object.keys(needSignToken).every(ckIdx => {
            let suTk = sucessTk[ckIdx];
            return suTk.includes(rtk);
        })
        // console.log('modifyToken 2 finishTk ret', rtk, ret)
        return ret;
    });
    removeTk.push(...finishTk);
    try {
        //保存未成功的token和明天需要签到的token
        console.log('老unsucessTk: ' + JSON.stringify(unsucessTk));
        //将unsucessTk写入配置
        unsucessTk[fileAllName] = unsucessTkItem;
        unsucessTk['day'] = ttoday;
        unsucessTk['queryFail'] = [...new Set(unsucessTk['queryFail'])].filter(tk => !removeTk.includes(tk) && !Object.keys(allTkInfo).includes(tk))

        var oldChangeToken = unsucessTk['changeToken'];
        if (oldChangeToken) {
            for (let [key, tks] of Object.entries(oldChangeToken)) {
                // 去除 removeTk 中的元素
                let filteredTks = tks.filter(tk => !removeTk.includes(tk));

                // 仅在过滤后的数组非空时进行合并
                if (filteredTks.length > 0) {
                    if (changeToken.hasOwnProperty(key)) {
                        changeToken[key] = changeToken[key].concat(filteredTks);
                    } else {
                        changeToken[key] = filteredTks;
                    }
                }
            }
        }
        for (const key in changeToken) {
            changeToken[key] = [...new Set(changeToken[key])]
        }
        unsucessTk['changeToken'] = changeToken
        console.log('新unsucessTk: ' + JSON.stringify(unsucessTk));
        fs.writeFileSync(unSignFile, JSON.stringify(unsucessTk, null, " "));
    } catch (e) {
        console.log('写入失败unsucessTk', e)
    }
    var notChangeother = hour == 0 && min < 10
    if (removeTk.length == 0 && notChangeother) {
        return
    }

    //删除失效的token
    console.log('removeTk', removeTk)
    //移除失效dpinfo
    dpinfo = Object.fromEntries(Object.entries(dpinfo).filter(([key, value]) => {
        if (removeTk.includes(key)) {
            return false;
        }
        // 计算两个星期几之间的差值
        let dayDifference = Math.abs(ttoday - value.ttoday);
        // 如果差值大于3，说明跨越了周末，需要调整
        if (dayDifference > 3) {
            dayDifference = 7 - dayDifference;
        }

        // 检查是否相差超过两天
        return dayDifference <= 2;
    }));
    try {
        //取所有tk
        var allTk = {
            "token": token.filter(tk => !removeTk.includes(tk)),
            "oneToken": oneToken.filter(tk => !removeTk.includes(tk)),
            "todayToken": todayToken.filter(tk => !removeTk.includes(tk))
        };
        // console.log('allTk', JSON.stringify(allTk, null , " "))
        if (!notChangeother) {
            tkey = Object.keys(changeToken).filter(value => value !== '' && value !== undefined)
            var ctk1 = []
            //保证最大的key对应的value在前面
            for (let i = tkey.length - 1; i >= 0; i--) {
                var tk = changeToken[tkey[i]];
                for (let index = 0; index < tk.length; index++) {
                    if (removeTk.includes(tk[index])) {
                        continue;
                    }
                    ctk1.push(tk[index]);
                }
            }
            ctk1 = [...new Set(ctk1)];
            var dp1AllTk = {
                "token": [],
                "oneToken": ctk1,
                "todayToken": []
            }
            dp1AllTk["token"].push(...allTk["token"]);
            dp1AllTk["token"].push(...allTk["todayToken"]);
            //过滤oneToken
            dp1AllTk["token"] = dp1AllTk["token"].filter(x => !ctk1.includes(x))
            dp1AllTk["token"] = [...new Set(dp1AllTk["token"])]
            allTk = dp1AllTk
            //更新环境变量
            var evlist = [...ctk1, ...dp1AllTk["token"]];
            modifyTkEv = evlist.join(',')
        }
        // 读取文件内容
        let content = fs.readFileSync(fileAllName, 'utf8');
        const lines = content.split('\n');
        //写入文件
        writeFile(lines, allTk, fileAllName)
    } catch (e) {
        console.log('修改文件失败', e)
    }
}

async function doLastTk() {
    //找到失败tk
    for (var [ckIdx, doTk] of Object.entries(needSignToken)) {
        //console.log(ckIdx + ' doTk: ' + JSON.stringify(doTk));
        suTk = sucessTk[ckIdx];
        // console.log('sucessTk', ckIdx, suTk)
        // console.log(ckIdx + ' suTk: ' + suTk);
        for (var needTk of doTk) {
            if (!(suTk.includes(needTk) || removeTk.includes(needTk))) {
                unsucessTkItem[ckIdx].push(needTk);
            }
        }
    }
    //设置重试并发1
    // await ev.set("dpqd_concNum", "1")
    await $.wait(1000);
    // console.log('unsucessTkItem', unsucessTkItem)

    let logDirs = [
        '/ql/data/log/6dylan6_jdpro_jd_dpqd_sign_1956/',
        '/ql/data/log/6dylan6_jdpro_jd_dpqd_sign_1955/',
        '/ql/data/log/6dylan6_jdpro_jd_dpqd_sign_1912/',
    ];
    let dirIndex = 0;
    for (var [ckIdx, tkList] of Object.entries(unsucessTkItem)) {
        if (tkList.length == 0) {
            continue;
        }
        //去重
        tkList = [...new Set(tkList)]
        unsucessTkItem[ckIdx] = tkList;
        //判断tkList中是否包含unsucessTk.queryFail
        var filterKeyList = Object.keys(dpinfo);
        //console.log('filterKeyList', filterKeyList)
        tkList = tkList.filter(tk => filterKeyList.includes(tk))
        if (tkList.length == 0) {
            continue;
        }
        //重跑失败的tk
        var reTk = tkList.join(',');
        if(tkList.length > 5) {
            var reTk;
            if (tkList.length > 5) {
                reTk = tkList.slice(0, 5).join(',');
            } else {
                reTk = tkList.join(',');
            }
        }
        await ev.set(dpEvName, reTk)
        await $.wait(1000);
        console.log(`账号${ckIdx} 重跑tk: ${reTk}`)
        //执行命令行
        var runTask = '6dylan6_jdpro/jd_dpqd_sign.js';
        var cmd = 'task ' + runTask + " desi JD_COOKIE " + ckIdx

        // 执行命令并将结果写入日志文件
        const result = spawnSync('task', [runTask, 'desi', 'JD_COOKIE', ckIdx], {
            encoding: 'utf8',
            stdio: ['inherit', 'pipe', 'pipe']
        });
        var exeResult = result.stdout + result.stderr;
        // var exeResult = ""
        await $.wait(1000);
        var now = new Date();
        if (exeResult.length > 0) {
            // 生成时间戳格式的文件名
            const timestamp = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}-${String(now.getHours()).padStart(2, '0')}-${String(now.getMinutes()).padStart(2, '0')}-${String(now.getSeconds()).padStart(2, '0')}-${String(now.getMilliseconds()).padStart(3, '0')}`;
            const logPath = `/ql/data/log/6dylan6_jdpro_jd_dpqd_sign_1912/${timestamp}.log`;
            //异步写入
            fs.promises.writeFile(logPath, exeResult).catch(err => {
                console.log('写入日志文件失败:', err);
            });
        } else {
            console.log('查找重试日志')
            while (dirIndex < 3) {
                var logDir = logDirs[dirIndex];
                var files = fs.readdirSync(logDir);
                var logFile = '';
                var logFiles = files.filter(file => file.endsWith('.log'));
                // 按文件名排序,取最新的
                logFiles.sort((a, b) => b.localeCompare(a));
                //2024-11-17-00-01-52-063.log
                logFile = logFiles[0];
                console.log('logDir', logDir, logFile)
                //判断文件名是否和当前时间相近
                // 将字符串转换为符合ISO 8601格式的字符串
                const match = logFile.match(/(\d{4})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{3})/);
                var formattedLogFileTimeString = `${match[1]}-${match[2]}-${match[3]}T${match[4]}:${match[5]}:${match[6]}.${match[7]}Z`;
                // 将字符串转换为Date对象
                var logFileTime = new Date(formattedLogFileTimeString);
                var nowMinutes = now.getMinutes();
                var nowHours = now.getHours();
                var logFileMinutes = logFileTime.getUTCMinutes();
                var logFileHours = logFileTime.getUTCHours();
                console.log('nowHours', nowHours == logFileHours && Math.abs(nowMinutes - logFileMinutes) <= 1, 
                    nowHours, nowMinutes, logFileHours, logFileMinutes)
                if (nowHours == logFileHours && Math.abs(nowMinutes - logFileMinutes) <= 1) {
                    exeResult = fs.readFileSync(logDir + logFile, 'utf8');
                    break;
                } else {
                    dirIndex++;
                }
            }
        }

        console.log('重跑tk结果', exeResult)

        // var exeResult = '';
        // var logDir = '/ql/data/log/6dylan6_jdpro_jd_dpqd_sign_1912/';
        // if(ckIdx == '1'){
        //     var logFile = logDir + '2024-11-03-00-02-53-982.log';
        //     // exeResult = fs.readFileSync(logFile, 'utf8');
        // } else if(ckIdx == '2'){
        //     var logFile = logDir + '2024-11-06-00-33-13-739.log';
        //     exeResult = fs.readFileSync(logFile, 'utf8');
        // }else if(ckIdx == '3'){
        //     var logFile = logDir + '2024-11-06-00-33-23-347.log';
        //     exeResult = fs.readFileSync(logFile, 'utf8');
        // }else if(ckIdx == '4'){
        //     var logFile = logDir + '2024-11-06-00-33-31-924.log';
        //     exeResult = fs.readFileSync(logFile, 'utf8');
        // }
        //解析结果
        analysisSignResults(exeResult);
    }
    //去除unsucessTkItem中重试成功的token
    for (var [ckIdx, unsuTkList] of Object.entries(unsucessTkItem)) {
        if (!unsuTkList?.length) {
            continue;
        }
        var suTk = sucessTk[ckIdx];
        // console.log('sucessTk', ckIdx, suTk)
        unsucessTkItem[ckIdx] = unsuTkList.filter(unsucessTk =>
            !(suTk.includes(unsucessTk) || removeTk.includes(unsucessTk)));
    }
}

function comboxQueryFile() {
    if (fs.existsSync(tkInfoFilebak) && doQuery) {
        console.log('合并文件');
        // 1. 读取tkInfoFilebak
        var oldtkStr = fs.readFileSync(tkInfoFilebak, 'utf8');
        var oldtkJson = JSON.parse(oldtkStr);
        // 2. 合并tkInfoFilebak和tkInfoFile
        allTkInfo = { ...oldtkJson, ...allTkInfo };
        // 过滤失效tk
        for (let key in allTkInfo) {
            if (removeTk.includes(key)) {
                delete allTkInfo[key];
            }
        }
        // 3. 保存新的tkInfoFile
        fs.writeFileSync(tkInfoFile, JSON.stringify(allTkInfo, null, " "));
        hebing = true
    }
}

function getLastFileContent(logDir) {
    //查找最新的日志文件
    var files = fs.readdirSync(logDir);
    var logFile = '';
    var logFiles = files.filter(file => file.endsWith('.log'));
    // 按文件名排序,取最新的
    logFiles.sort((a, b) => b.localeCompare(a));
    logFile = logDir + logFiles[0];
    //logFile = logDir + '2024-11-01-23-18-26-775.log';
    // console.log('logFile', logFile)
    if (logFile == '') {
        throw new Error('未找到日志文件')
    }
    //读取日志文件
    var content = fs.readFileSync(logFile, 'utf8');
    return content
}

async function doRuntask(runTask, log) {
    //var runTask = 'console.js';
    //执行tk批量查询
    console.log('执行任务 run', log);
    await crons.runByName(runTask);
    await $.wait(2000);
    //循环等到的查询结束
    var isRunning = true;
    while (isRunning) {
        var taskInfoList = await crons.get(runTask);
        var taskInfo = taskInfoList[0];
        //console.log('taskInfo', taskInfo)
        isRunning = taskInfo["status"] == 0 ? true : false;
        if (isRunning) {
            await $.wait(10000);
        }
    }
    console.log('执行任务 end', log);
}

function writeFile(lines, allTk, fileAllName) {
    console.log('写文件', fileAllName)
    //准备写入文本
    var writeLines = '';
    var start = false, tkEnd = false;
    for (var line of lines) {
        if (start) {
            if (!line.includes('theEnd')) {
                continue;
            }
            //theEnd时写入tk
            tkEnd = true;
            start = false;

            var tkLine = '';
            for (var [tkName, tkList] of Object.entries(allTk)) {
                tkLine += 'const ' + tkName + ' = [\n';
                for (const tk of tkList) {
                    tkLine += '"' + tk + '",\n';
                }
                //删除最后一个,
                if (tkLine.endsWith('",\n')) {
                    tkLine = tkLine.substring(0, tkLine.length - 2) + '\n'
                }
                tkLine += ']\n'
            }
            writeLines += tkLine + "\n";
            //写入店铺信息
            writeLines += 'dpinfo = ' + JSON.stringify(dpinfo) + "\n";
            writeLines += line + "\n";
        } else if (tkEnd) {
            //theEnd 往后
            writeLines += line + "\n";
        } else {
            //从开始到碰到token = [
            var match = line.match(' (token|oneToken|todayToken)+.*=')
            if (match) {
                start = true;
            } else {
                writeLines += line + "\n";
            }
        }
    }
    writeLines = writeLines.trimEnd();
    //写入
    fs.writeFileSync(fileAllName, writeLines);
}


function jsonParse(str) {
    if (typeof str == "string") {
        try {
            return JSON.parse(str);
        } catch (e) {
            console.log(e);
            $.msg($.name, '', '请勿随意在BoxJs输入框修改内容\n建议通过脚本去获取cookie')
            return [];
        }
    }
}

// prettier-ignore
function Env(t, e) { class s { constructor(t) { this.env = t } send(t, e = "GET") { t = "string" == typeof t ? { url: t } : t; let s = this.get; return "POST" === e && (s = this.post), new Promise((e, i) => { s.call(this, t, (t, s, r) => { t ? i(t) : e(s) }) }) } get(t) { return this.send.call(this.env, t) } post(t) { return this.send.call(this.env, t, "POST") } } return new class { constructor(t, e) { this.name = t, this.http = new s(this), this.data = null, this.dataFile = "box.dat", this.logs = [], this.isMute = !1, this.isNeedRewrite = !1, this.logSeparator = "\n", this.startTime = (new Date).getTime(), Object.assign(this, e), this.log("", `\ud83d\udd14${this.name}, \u5f00\u59cb!`) } isNode() { return "undefined" != typeof module && !!module.exports } isQuanX() { return "undefined" != typeof $task } isSurge() { return "undefined" != typeof $httpClient && "undefined" == typeof $loon } isLoon() { return "undefined" != typeof $loon } toObj(t, e = null) { try { return JSON.parse(t) } catch { return e } } toStr(t, e = null) { try { return JSON.stringify(t) } catch { return e } } getjson(t, e) { let s = e; const i = this.getdata(t); if (i) try { s = JSON.parse(this.getdata(t)) } catch { } return s } setjson(t, e) { try { return this.setdata(JSON.stringify(t), e) } catch { return !1 } } getScript(t) { return new Promise(e => { this.get({ url: t }, (t, s, i) => e(i)) }) } runScript(t, e) { return new Promise(s => { let i = this.getdata("@chavy_boxjs_userCfgs.httpapi"); i = i ? i.replace(/\n/g, "").trim() : i; let r = this.getdata("@chavy_boxjs_userCfgs.httpapi_timeout"); r = r ? 1 * r : 20, r = e && e.timeout ? e.timeout : r; const [o, h] = i.split("@"), a = { url: `http://${h}/v1/scripting/evaluate`, body: { script_text: t, mock_type: "cron", timeout: r }, headers: { "X-Key": o, Accept: "*/*" } }; this.post(a, (t, e, i) => s(i)) }).catch(t => this.logErr(t)) } loaddata() { if (!this.isNode()) return {}; { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), i = !s && this.fs.existsSync(e); if (!s && !i) return {}; { const i = s ? t : e; try { return JSON.parse(this.fs.readFileSync(i)) } catch (t) { return {} } } } } writedata() { if (this.isNode()) { this.fs = this.fs ? this.fs : require("fs"), this.path = this.path ? this.path : require("path"); const t = this.path.resolve(this.dataFile), e = this.path.resolve(process.cwd(), this.dataFile), s = this.fs.existsSync(t), i = !s && this.fs.existsSync(e), r = JSON.stringify(this.data); s ? this.fs.writeFileSync(t, r) : i ? this.fs.writeFileSync(e, r) : this.fs.writeFileSync(t, r) } } lodash_get(t, e, s) { const i = e.replace(/\[(\d+)\]/g, ".$1").split("."); let r = t; for (const t of i) if (r = Object(r)[t], void 0 === r) return s; return r } lodash_set(t, e, s) { return Object(t) !== t ? t : (Array.isArray(e) || (e = e.toString().match(/[^.[\]]+/g) || []), e.slice(0, -1).reduce((t, s, i) => Object(t[s]) === t[s] ? t[s] : t[s] = Math.abs(e[i + 1]) >> 0 == +e[i + 1] ? [] : {}, t)[e[e.length - 1]] = s, t) } getdata(t) { let e = this.getval(t); if (/^@/.test(t)) { const [, s, i] = /^@(.*?)\.(.*?)$/.exec(t), r = s ? this.getval(s) : ""; if (r) try { const t = JSON.parse(r); e = t ? this.lodash_get(t, i, "") : e } catch (t) { e = "" } } return e } setdata(t, e) { let s = !1; if (/^@/.test(e)) { const [, i, r] = /^@(.*?)\.(.*?)$/.exec(e), o = this.getval(i), h = i ? "null" === o ? null : o || "{}" : "{}"; try { const e = JSON.parse(h); this.lodash_set(e, r, t), s = this.setval(JSON.stringify(e), i) } catch (e) { const o = {}; this.lodash_set(o, r, t), s = this.setval(JSON.stringify(o), i) } } else s = this.setval(t, e); return s } getval(t) { return this.isSurge() || this.isLoon() ? $persistentStore.read(t) : this.isQuanX() ? $prefs.valueForKey(t) : this.isNode() ? (this.data = this.loaddata(), this.data[t]) : this.data && this.data[t] || null } setval(t, e) { return this.isSurge() || this.isLoon() ? $persistentStore.write(t, e) : this.isQuanX() ? $prefs.setValueForKey(t, e) : this.isNode() ? (this.data = this.loaddata(), this.data[e] = t, this.writedata(), !0) : this.data && this.data[e] || null } initGotEnv(t) { this.got = this.got ? this.got : require("got"), this.cktough = this.cktough ? this.cktough : require("tough-cookie"), this.ckjar = this.ckjar ? this.ckjar : new this.cktough.CookieJar, t && (t.headers = t.headers ? t.headers : {}, void 0 === t.headers.Cookie && void 0 === t.cookieJar && (t.cookieJar = this.ckjar)) } get(t, e = (() => { })) { t.headers && (delete t.headers["Content-Type"], delete t.headers["Content-Length"]), this.isSurge() || this.isLoon() ? (this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.get(t, (t, s, i) => { !t && s && (s.body = i, s.statusCode = s.status), e(t, s, i) })) : this.isQuanX() ? (this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: i, headers: r, body: o } = t; e(null, { status: s, statusCode: i, headers: r, body: o }, o) }, t => e(t))) : this.isNode() && (this.initGotEnv(t), this.got(t).on("redirect", (t, e) => { try { if (t.headers["set-cookie"]) { const s = t.headers["set-cookie"].map(this.cktough.Cookie.parse).toString(); this.ckjar.setCookieSync(s, null), e.cookieJar = this.ckjar } } catch (t) { this.logErr(t) } }).then(t => { const { statusCode: s, statusCode: i, headers: r, body: o } = t; e(null, { status: s, statusCode: i, headers: r, body: o }, o) }, t => { const { message: s, response: i } = t; e(s, i, i && i.body) })) } post(t, e = (() => { })) { if (t.body && t.headers && !t.headers["Content-Type"] && (t.headers["Content-Type"] = "application/x-www-form-urlencoded"), t.headers && delete t.headers["Content-Length"], this.isSurge() || this.isLoon()) this.isSurge() && this.isNeedRewrite && (t.headers = t.headers || {}, Object.assign(t.headers, { "X-Surge-Skip-Scripting": !1 })), $httpClient.post(t, (t, s, i) => { !t && s && (s.body = i, s.statusCode = s.status), e(t, s, i) }); else if (this.isQuanX()) t.method = "POST", this.isNeedRewrite && (t.opts = t.opts || {}, Object.assign(t.opts, { hints: !1 })), $task.fetch(t).then(t => { const { statusCode: s, statusCode: i, headers: r, body: o } = t; e(null, { status: s, statusCode: i, headers: r, body: o }, o) }, t => e(t)); else if (this.isNode()) { this.initGotEnv(t); const { url: s, ...i } = t; this.got.post(s, i).then(t => { const { statusCode: s, statusCode: i, headers: r, body: o } = t; e(null, { status: s, statusCode: i, headers: r, body: o }, o) }, t => { const { message: s, response: i } = t; e(s, i, i && i.body) }) } } time(t) { let e = { "M+": (new Date).getMonth() + 1, "d+": (new Date).getDate(), "H+": (new Date).getHours(), "m+": (new Date).getMinutes(), "s+": (new Date).getSeconds(), "q+": Math.floor(((new Date).getMonth() + 3) / 3), S: (new Date).getMilliseconds() }; /(y+)/.test(t) && (t = t.replace(RegExp.$1, ((new Date).getFullYear() + "").substr(4 - RegExp.$1.length))); for (let s in e) new RegExp("(" + s + ")").test(t) && (t = t.replace(RegExp.$1, 1 == RegExp.$1.length ? e[s] : ("00" + e[s]).substr(("" + e[s]).length))); return t } msg(e = t, s = "", i = "", r) { const o = t => { if (!t) return t; if ("string" == typeof t) return this.isLoon() ? t : this.isQuanX() ? { "open-url": t } : this.isSurge() ? { url: t } : void 0; if ("object" == typeof t) { if (this.isLoon()) { let e = t.openUrl || t.url || t["open-url"], s = t.mediaUrl || t["media-url"]; return { openUrl: e, mediaUrl: s } } if (this.isQuanX()) { let e = t["open-url"] || t.url || t.openUrl, s = t["media-url"] || t.mediaUrl; return { "open-url": e, "media-url": s } } if (this.isSurge()) { let e = t.url || t.openUrl || t["open-url"]; return { url: e } } } }; this.isMute || (this.isSurge() || this.isLoon() ? $notification.post(e, s, i, o(r)) : this.isQuanX() && $notify(e, s, i, o(r))); let h = ["", "==============\ud83d\udce3\u7cfb\u7edf\u901a\u77e5\ud83d\udce3=============="]; h.push(e), s && h.push(s), i && h.push(i), console.log(h.join("\n")), this.logs = this.logs.concat(h) } log(...t) { t.length > 0 && (this.logs = [...this.logs, ...t]), console.log(t.join(this.logSeparator)) } logErr(t, e) { const s = !this.isSurge() && !this.isQuanX() && !this.isLoon(); s ? this.log("", `\u2757\ufe0f${this.name}, \u9519\u8bef!`, t.stack) : this.log("", `\u2757\ufe0f${this.name}, \u9519\u8bef!`, t) } wait(t) { return new Promise(e => setTimeout(e, t)) } done(t = {}) { const e = (new Date).getTime(), s = (e - this.startTime) / 1e3; this.log("", `\ud83d\udd14${this.name}, \u7ed3\u675f! \ud83d\udd5b ${s} \u79d2`), this.log(), (this.isSurge() || this.isQuanX() || this.isLoon()) && $done(t) } }(t, e) }